// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;

namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath;

public class ClampInt32IntoByte
{
    [Params(-1, 0, 255, 256)]
    public int Value { get; set; }

    [Benchmark(Baseline = true, Description = "Maths Clamp")]
    public byte ClampMaths()
    {
       int value = this.Value;
       return (byte)Math.Min(Math.Max(byte.MinValue, value), byte.MaxValue);
    }

    [Benchmark(Description = "No Maths Clamp")]
    public byte ClampNoMaths()
    {
       int value = this.Value;
       value = value >= byte.MaxValue ? byte.MaxValue : value;
       return (byte)(value <= byte.MinValue ? byte.MinValue : value);
    }

    [Benchmark(Description = "No Maths No Equals Clamp")]
    public byte ClampNoMathsNoEquals()
    {
       int value = this.Value;
       value = value > byte.MaxValue ? byte.MaxValue : value;
       return (byte)(value < byte.MinValue ? byte.MinValue : value);
    }

    [Benchmark(Description = "No Maths Clamp No Ternary")]
    public byte ClampNoMathsNoTernary()
    {
        int value = this.Value;

        if (value >= byte.MaxValue)
        {
            return byte.MaxValue;
        }

        if (value <= byte.MinValue)
        {
            return byte.MinValue;
        }

        return (byte)value;
    }

    [Benchmark(Description = "No Maths No Equals Clamp No Ternary")]
    public byte ClampNoMathsEqualsNoTernary()
    {
       int value = this.Value;

       if (value > byte.MaxValue)
       {
           return byte.MaxValue;
       }

       if (value < byte.MinValue)
       {
           return byte.MinValue;
       }

       return (byte)value;
    }

    [Benchmark(Description = "Clamp using Bitwise Abs")]
    public byte ClampBitwise()
    {
        int x = this.Value;
        int absMax = byte.MaxValue - x;
        x = (x + byte.MaxValue - AbsBitwiseVer(ref absMax)) >> 1;
        x = (x + byte.MinValue + AbsBitwiseVer(ref x)) >> 1;

        return (byte)x;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static int AbsBitwiseVer(ref int x)
    {
        int y = x >> 31;
        return (x ^ y) - y;
    }
}
